package io.shuttle.mbe.api

import io.shuttle.mbe.api.annotation.ChromeMinVersion
import io.shuttle.mbe.api.annotation.PromiseStyleMinVersion
import io.shuttle.mbe.api.types.Value1Function
import io.shuttle.mbe.api.types.Value2Function
import io.shuttle.mbe.api.types.VoidFunction
import io.shuttle.mbe.core.Promise

////////////////////
// Identity
////////////////////
/**
 * Use the `Browser.identity` API to get OAuth2 access tokens.
 *
 * Permissions: "identity"
 */
interface Identity {
    // Resets the state of the Identity API:
    //
    //Removes all OAuth2 access tokens from the token cache
    //Removes user's account preferences
    //De-authorizes the user from all auth flows
    @ChromeMinVersion(87)
    @PromiseStyleMinVersion(106)
    fun clearAllCachedAuthTokens(callback: VoidFunction? = null): Promise<Void>

    // Retrieves a list of AccountInfo objects describing the accounts present on the profile.
    //
    //getAccounts is only supported on dev channel.
    @PromiseStyleMinVersion(106)
    fun getAccounts(
        callback: Value1Function<List<AccountInfo>>? = null
    ): Promise<List<AccountInfo>>

    // Gets an OAuth2 access token using the client ID and scopes specified in the oauth2 section of manifest.json.
    //
    //The Identity API caches access tokens in memory, so it's ok to call getAuthToken non-interactively any time a token is required.
    // The token cache automatically handles expiration.
    //
    //For a good user experience it is important interactive token requests are initiated by UI in your app explaining what the authorization is for.
    // Failing to do this will cause your users to get authorization requests, or Chrome sign in screens if they are not signed in, with with no context.
    // In particular, do not use getAuthToken interactively when your app is first launched.
    //
    //Note: When called with a callback, instead of returning an object this function will return the two properties as separate arguments passed to the callback.
    @PromiseStyleMinVersion(105)
    fun getAuthToken(
        details: TokenDetails?,
        callback: Value1Function<GetAuthTokenResult>? = null
    ): Promise<GetAuthTokenResult>

    // Retrieves email address and obfuscated gaia id of the user signed into a profile.
    //
    //Requires the identity.email manifest permission. Otherwise, returns an empty result.
    //
    //This API is different from identity.getAccounts in two ways.
    // The information returned is available offline, and it only applies to the primary account for the profile.
    @PromiseStyleMinVersion(106)
    fun getProfileUserInfo(
        // Profile options.
        @ChromeMinVersion(84)
        details: ProfileDetails?,
        callback: Value1Function<ProfileUserInfo>? = null
    ): Promise<ProfileUserInfo>

    // Generates a redirect URL to be used in launchWebAuthFlow.
    //
    //The generated URLs match the pattern https://<app-id>.chromiumapp.org/*.
    fun getRedirectURL(
        // The path appended to the end of the generated URL.
        path: String?,
    ): String

    // Starts an auth flow at the specified URL.
    //
    //This method enables auth flows with non-Google identity providers by launching a web view and navigating it to the first URL in the provider's auth flow.
    // When the provider redirects to a URL matching the pattern https://<app-id>.chromiumapp.org/*, the window will close,
    // and the final redirect URL will be passed to the callback function.
    //
    //For a good user experience it is important interactive auth flows are initiated by UI in your app explaining what the authorization is for.
    // Failing to do this will cause your users to get authorization requests with no context.
    // In particular, do not launch an interactive auth flow when your app is first launched.
    // @return {callback.responseUrl}
    @PromiseStyleMinVersion(106)
    fun launchWebAuthFlow(
        details: WebAuthFlowDetails,
        callback: Value1Function<String?>? = null
    ): Promise<String?>

    // Removes an OAuth2 access token from the Identity API's token cache.
    //
    //If an access token is discovered to be invalid, it should be passed to removeCachedAuthToken to remove it from the cache.
    // The app may then retrieve a fresh token with getAuthToken.
    @PromiseStyleMinVersion(106)
    fun removeCachedAuthToken(
        details: InvalidTokenDetails,
        callback: VoidFunction? = null
    ): Promise<Void>

    val onSignInChanged: Events.Event<Value2Function<AccountInfo, Boolean>>

    data class AccountInfo(
        // A unique identifier for the account. This ID will not change for the lifetime of the account.
        var id: String,
    )

    @ChromeMinVersion(84)
    enum class AccountStatus {
        // Specifies that Sync is enabled for the primary account.
        SYNC,

        // Specifies the existence of a primary account, if any.
        ANY
    }

    @ChromeMinVersion(105)
    data class GetAuthTokenResult(
        // A list of OAuth2 scopes granted to the extension.
        var grantedScopes: List<String>?,
        // The specific token associated with the request.
        var token: String?,
    )

    data class InvalidTokenDetails(
        // The specific token that should be removed from the cache.
        var token: String,
    )

    @ChromeMinVersion(84)
    data class ProfileDetails(
        // A status of the primary account signed into a profile whose ProfileUserInfo should be returned. Defaults to SYNC account status.
        var accountStatus: AccountStatus? = AccountStatus.SYNC,
    )

    data class ProfileUserInfo(
        // An email address for the user account signed into the current profile.
        // Empty if the user is not signed in or the identity.email manifest permission is not specified.
        var email: String,
        // A unique identifier for the account. This ID will not change for the lifetime of the account.
        // Empty if the user is not signed in or (in M41+) the identity.email manifest permission is not specified.
        var id: String,
    )

    data class TokenDetails(
        // The account ID whose token should be returned.
        // If not specified, the function will use an account from the Chrome profile:
        // the Sync account if there is one, or otherwise the first Google web account.
        var accountStatus: AccountInfo,
        // The enableGranularPermissions flag allows extensions to opt-in early to the granular permissions consent screen,
        // in which requested permissions are granted or denied individually.
        @ChromeMinVersion(87)
        var enableGranularPermissions: Boolean?,
        // Fetching a token may require the user to sign-in to Chrome, or approve the application's requested scopes.
        // If the interactive flag is true, getAuthToken will prompt the user as necessary.
        // When the flag is false or omitted, getAuthToken will return failure any time a prompt would be required.
        var interactive: Boolean?,
        // A list of OAuth2 scopes to request.
        //
        //When the scopes field is present, it overrides the list of scopes specified in manifest.json.
        var scopes: List<String>?,
    )

    data class WebAuthFlowDetails(
        // Whether to terminate launchWebAuthFlow for non-interactive requests after the page loads.
        // This parameter does not affect interactive flows.
        //
        //When set to true (default) the flow will terminate immediately after the page loads.
        // When set to false, the flow will only terminate after the timeoutMsForNonInteractive passes.
        // This is useful for identity providers that use JavaScript to perform redirections after the page loads.
        @ChromeMinVersion(113)
        var abortOnLoadForNonInteractive: Boolean?,
        // Whether to launch auth flow in interactive mode.
        //
        //Since some auth flows may immediately redirect to a result URL,
        // launchWebAuthFlow hides its web view until the first navigation either redirects to the final URL,
        // or finishes loading a page meant to be displayed.
        //
        //If the interactive flag is true, the window will be displayed when a page load completes.
        // If the flag is false or omitted, launchWebAuthFlow will return with an error if the initial navigation does not complete the flow.
        //
        //For flows that use JavaScript for redirection,
        // abortOnLoadForNonInteractive can be set to false in combination with setting timeoutMsForNonInteractive to give the page a chance to perform any redirects.
        var interactive: Boolean?,
        // The maximum amount of time, in miliseconds, launchWebAuthFlow is allowed to run in non-interactive mode in total.
        // Only has an effect if interactive is false.
        @ChromeMinVersion(113)
        var timeoutMsForNonInteractive: Number?,
        // The URL that initiates the auth flow.
        var url: String,
    )
}